package com.gitee.wsl.security.hash

import com.gitee.wsl.ext.base.Arrays.arraycopy


typealias Word = Int
typealias WordArray = IntArray
internal fun wordArrayOf(vararg elements: Word): WordArray {
    val wordArray = WordArray(elements.size)
    elements.forEachIndexed { idx, word ->
        wordArray[idx] = word
    }
    return wordArray
}

internal fun Long.toWord(): Word {
    return this.toInt()
}

object SM3 {

    const val SM3_BLOCK_SIZE: Int = 64
    const val SM3_DIGEST_LEN: Int = 32
    const val SM3_HMAC_LEN: Int = 32

    val iv: ByteArray = byteArrayOf(
        0x73, 0x80.toByte(), 0x16, 0x6f, 0x49,
        0x14, 0xb2.toByte(), 0xb9.toByte(), 0x17, 0x24, 0x42, 0xd7.toByte(),
        0xda.toByte(), 0x8a.toByte(), 0x06, 0x00, 0xa9.toByte(), 0x6f, 0x30,
        0xbc.toByte(), 0x16.toByte(), 0x31, 0x38, 0xaa.toByte(), 0xe3.toByte(),
        0x8d.toByte(), 0xee.toByte(), 0x4d, 0xb0.toByte(), 0xfb.toByte(), 0x0e,
        0x4e
    )


    /**
     * SM3算法
     *
     * @param message 消息
     * @return 结果
     */
    fun hash(message: ByteArray): ByteArray {
        val paddedMsg = messagePadding(message) //消息填充
        return iterativeCompression(paddedMsg) //迭代压缩
    }

    /**
     * HmacSM3算法
     *
     * @param message 消息
     * @param key     秘钥
     * @return 结果
     */
    fun createHmac(message: ByteArray, key: ByteArray): ByteArray {
        val LENGTH = 64
        val actualKey: ByteArray
        val zeroLength = LENGTH - key.size
        val zero = ByteArray(zeroLength)
        for (i in 0..<zeroLength) {
            zero[i] = 0x00.toByte()
        }
        if (key.size > 64) {
            actualKey = hash(key)
        } else {
            actualKey = bytesMerge(key, zero)
        }
        val iPadXOR = ByteArray(LENGTH)
        val oPadXOR = ByteArray(LENGTH)
        for (i in 0..<LENGTH) {
            iPadXOR[i] = (actualKey[i].toInt() xor 0x36).toByte()
            oPadXOR[i] = (actualKey[i].toInt() xor 0x5c).toByte()
        }
        val firstMerge = bytesMerge(iPadXOR, message)
        val firstHash = hash(firstMerge)
        val secondMerge = bytesMerge(oPadXOR, firstHash)
        return hash(secondMerge)
    }

    /**
     * 消息填充
     * 把消息填充到512比特倍数的消息
     *
     * @param msg 消息
     * @return 512比特倍数的消息
     */
    private fun messagePadding(msg: ByteArray): ByteArray {
        val keyLength = msg.size.toLong()
        val BIT = byteArrayOf(0x80.toByte())
        val outKeyLength = (keyLength % 64).toInt()
        val zeroLength = if (outKeyLength < 55) (55 - outKeyLength) else (55 - outKeyLength + 64)
        val binaryMessageLength = longToBytes(keyLength * 8)
        val zero = ByteArray(zeroLength)
        for (i in 0..<zeroLength) {
            zero[i] = 0x00.toByte()
        }
        return bytesMerge(bytesMerge(bytesMerge(msg, BIT), zero), binaryMessageLength)
    }

    /**
     * 消息扩展
     * 消息分组扩展成132个字
     *
     * @param word 字消息分组
     * @return 扩展后的字分组
     */
    private fun extendMessage(word: IntArray): Array<IntArray> {
        val w = IntArray(68)
        val w1 = IntArray(64)
        for (i in word.indices) {
            w[i] = word[i]
        }
        for (j in 16..67) {
            w[j] = P1(w[j - 16] xor w[j - 9] xor CircleLeftShift(w[j - 3], 15)) xor CircleLeftShift(
                w[j - 13],
                7
            ) xor w[j - 6]
        }
        for (j in 0..63) {
            w1[j] = w[j] xor w[j + 4]
        }
        return arrayOf(w,w1)
    }

    /**
     * 迭代压缩
     * 把填充后的消息按512比特分组,然后按照文档给的方式压缩迭代
     *
     * @param paddedMsg 填充后的消息
     * @return 摘要结果
     */
    private fun iterativeCompression(paddedMsg: ByteArray): ByteArray {
        val word = IntArray(16)
        val n = (paddedMsg.size) / 64
        var VI = intArrayOf(
            0x7380166f,
            0x4914b2b9,
            0x172442d7,
            -0x2575fa00,
            -0x5690cf44,
            0x163138aa,
            -0x1c7211b3,
            -0x4f04f1b2
        )
        var VX = IntArray(8)
        for (i in 0..<n) {
            try {
                val bytes: ByteArray = paddedMsg.copyOfRange(64 * i, 64 * (i + 1))
                for (j in 0..15) {
                    word[j] = bytesToWord(bytes, j * 4)
                }
                VI = CF(VI, word)
                VX = VI
            } catch (e: Exception) {
                e.printStackTrace()
            }
        }
        return wordsToBytes(VX)
    }

    /**
     * long型转byte数组
     *
     * @param num 数
     * @return byte数组
     */
    private fun longToBytes(num: Long): ByteArray {
        val bytes = ByteArray(8)
        for (i in 0..7) {
            val offset = (7 - i) * 8
            bytes[i] = ((num ushr offset) and 0xffL).toByte()
        }
        return bytes
    }

    /**
     * 字转byte数组
     *
     * @param words 字（长度为32的比特串）
     * @return byte数组
     */
    private fun wordsToBytes(words: IntArray): ByteArray {
        val bytes = ByteArray(32)
        for (i in words.indices) {
            for (j in 0..3) {
                val offset = (3 - j) * 8
                bytes[j + (i * 4)] = ((words[i] ushr offset) and 0xff).toByte()
            }
        }
        return bytes
    }

    /**
     * byte数组转字
     *
     * @param bytes byte数组
     * @param index 数组下标
     * @return 字
     */
    private fun bytesToWord(bytes: ByteArray, index: Int): Int {
        val result: Int = ((bytes[index].toInt() and 0xff) shl 24) or ((bytes[index + 1].toInt() and 0xff) shl 16) or ((bytes[index + 2].toInt() and 0xff) shl 8) or ((bytes[index + 3].toInt() and 0xff))
        return result
    }

    /**
     * byte数组转16进制字符串
     *
     * @param bytes bytes数组
     * @return 16进制字符串
     */
    fun bytesToHexString(bytes: ByteArray): String {
        val hexDigits = charArrayOf('0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f')
        val buffer = StringBuilder()
        for (b in bytes) {
            var c = hexDigits[(b.toInt() ushr 4) and 0xf]
            buffer.append(c)
            c = hexDigits[b.toInt() and 0xf]
            buffer.append(c)
        }
        return buffer.toString()
    }

    /**
     * 数组拼接
     *
     * @param front 拼接在前面的数组
     * @param rear  拼接在后面的数组
     * @return 拼接后的数组
     */
    private fun bytesMerge(front: ByteArray, rear: ByteArray): ByteArray {
        val bytes: ByteArray = front.copyOf( front.size + rear.size)
        arraycopy(rear, 0, bytes, front.size, rear.size)
        return bytes
    }

    /**
     * 压缩函数
     *
     * @param VI   记录压缩函数寄存器的初态
     * @param word 中间值
     * @return 压缩后结果
     */
    internal fun CF(VI: IntArray, word: IntArray): IntArray {
        val wx = extendMessage(word)
        val w = wx[0]
        val w1 = wx[1]
        val result = IntArray(8)
        var A: Int
        var B: Int
        var C: Int
        var D: Int
        var E: Int
        var F: Int
        var G: Int
        var H: Int
        var SS1: Int
        var SS2: Int
        var TT1: Int
        var TT2: Int
        A = VI[0]
        B = VI[1]
        C = VI[2]
        D = VI[3]
        E = VI[4]
        F = VI[5]
        G = VI[6]
        H = VI[7]
        for (j in 0..63) {
            SS1 = CircleLeftShift(CircleLeftShift(A, 12) + E + CircleLeftShift(T(j), j), 7)
            SS2 = SS1 xor CircleLeftShift(A, 12)
            TT1 = FF(A, B, C, j) + D + SS2 + w1[j]
            TT2 = GG(E, F, G, j) + H + SS1 + w[j]
            D = C
            C = CircleLeftShift(B, 9)
            B = A
            A = TT1
            H = G
            G = CircleLeftShift(F, 19)
            F = E
            E = P0(TT2)
        }
        result[0] = A xor VI[0]
        result[1] = B xor VI[1]
        result[2] = C xor VI[2]
        result[3] = D xor VI[3]
        result[4] = E xor VI[4]
        result[5] = F xor VI[5]
        result[6] = G xor VI[6]
        result[7] = H xor VI[7]
        return result
    }

    /**
     * 常量，随j变化而变化
     *
     * @param j 组数
     * @return 结果
     */
    private fun T(j: Int): Int {
        if (j <= 15) {
            return 0x79cc4519
        } else {
            return 0x7a879d8a
        }
    }

    /**
     * 布尔函数
     *
     * @param x 待运算数
     * @param y 待运算数
     * @param z 待运算数
     * @param j 组数
     * @return 运算结果
     */
    private fun FF(x: Int, y: Int, z: Int, j: Int): Int {
        var result = 0
        if (j >= 0 && j <= 15) {
            result = x xor y xor z
        } else if (j >= 16 && j <= 63) {
            result = (x and y) or (x and z) or (y and z)
        }
        return result
    }

    /**
     * 布尔函数
     *
     * @param x 待运算数
     * @param y 待运算数
     * @param z 待运算数
     * @param j 待运算数
     * @return 运算结果
     */
    private fun GG(x: Int, y: Int, z: Int, j: Int): Int {
        var result = 0
        if (j >= 0 && j <= 15) {
            result = x xor y xor z
        } else if (j >= 16 && j <= 63) {
            result = (x and y) or (x.inv() and z)
        }
        return result
    }

    /**
     * 旋转左移位运算
     *
     * @param x 待运算数
     * @param n 变化量
     * @return 旋转左移位运算结果
     */
    private fun CircleLeftShift(x: Int, n: Int): Int {
        return (x shl n) or (x ushr (32 - n))
    }

    /**
     * 压缩函数中的置换函数
     *
     * @param x 被置换数
     * @return 置换结果
     */
    private fun P0(x: Int): Int {
        return x xor CircleLeftShift(x, 9) xor CircleLeftShift(x, 17)
    }

    /**
     * 消息扩展中的置换函数
     *
     * @param x 被置换数
     * @return 置换结果
     */
    private fun P1(x: Int): Int {
        return x xor CircleLeftShift(x, 15) xor CircleLeftShift(x, 23)
    }
}

fun ByteArray.sm3() = SM3.hash(this)

fun ByteArray.hmacSM3(key: ByteArray) = SM3.createHmac(this,key)